List中有三個實作類
array
,但是可以把它當成動態
的array,當新增長度不夠時,底層
會自動擴充長度,因為有index
適用於查詢
元素。插入
、刪除
元素。List
類,後來在jdk5.0後新增了List
、ArrayList
後,把它納入List
底下,基本上已經很少使用此類。常用的方法
void add(int index, Object ele)
:在index處新增一個元素。(註:ele代表element
元素,只是一個約定的命名方式,較為簡潔,不是必要)boolean addAll(int index, Collection eles)
:在索引處新增一個集合。Object get(int index)
:獲得index處的一個元素。List subList(int fromIndex, int toIndex)
:返回一個從fromIndex
到toIndex
的子集合。int indexOf(Object obj)
:返回首次
出現物件的索引。int lastIndexOf(Object obj)
:返回最後
出現物件的索引。Object remove(int index)
:刪除指定index處的物件,並將它返回。Object set(int index, Object ele)
:將指定index處物件換成ele
,並返回被取代
的物件。比較要小心的是remove(int index)
方法
ArrayList list = new ArrayList();
list.add("abc");
list.add("bbb");
list.add(123);
list.remove("abc");
//list.remove(123); 當使用這樣想刪除123時,會出現錯誤
Collection
只能存放引用類別
的資料,當新增基本類別
的資料時,會用包裝類將它們包裝成引用類別
,所以如果要刪除123
時,直接在參數中放入123
會認為是要刪除位於index
123的資料。
必須這樣使用才能夠正確地將123
刪除
list.remove(Integer.valueOf(123));
Set中有三個實作類
HashMap
的資料結構(之後會說)。HashSet
的subclass
,多加了雙向鏈表
紀錄元素新增的先後順序,所以可以按照元素新增的順序進行遍歷,也可以用於頻繁的查詢。資料型別
必須相同
,否則會出現ClassCastException
異常。Set中沒有另外新增額外的方法,都是使用Collection
的方法。
這兩個Set
的實作類是使用hashCode()
和equals()
方法去判斷是否存在重複,因此在新增自定義的類
時,需要將hashCode()
和equals()
進行重寫。
class Person {
String name;
int age;
}
HashSet set = new HashSet();
set.add(new Person("Tom",30));
set.add(new Person("Tom",30));
Ierator iterator = set.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
Person{name='Tom', age=30}
Person{name='Tom', age=30}
由於沒有重寫hashCode()
和equals()
方法,所以這兩個新增的Person類
明明是相同的內容(記憶體中的地址不同),卻還是加到Set
中了
Person類修改(重寫hashCode()
和equals()
方法)
class Person {
//...略
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
這時候如果再重複新增一次時,就只會顯示一個Person{name='Tom', age=30}
了。
它的原理是這樣的:
hash算法
後放入一個陣列
中的某個位置(不會按照陣列的索引擺放,所以是無序
)hash
後要放入的陣列
索引處已經有東西時,會使用equals()
進行比較,如果不相等時,才會將這個類放入Set
裡Set
是無序
且不可重複
的,但是TreeSet
在判斷是否重複時跟HashSet
、LinkedHashSet
判斷的方式不同,它是已compareTo()
、compare()
去判斷元素是否重複,所以如果是放入自定義的類
時,需要將該類實現Comparable
接口或是另外實作Comparator
類進行判斷。
TreeSet:加入不同類型的元素時,運行會報異常(編譯不會)
TreeSet set = new TreeSet();
set.add("aa");
set.add("bb");
set.add("cc");
//set.add(123); ClassCastException
假設目前有一個User
類(實作Comparable
接口)
class User implements Comparable {
String name;
int age;
// 為了避免內容過於複雜,省略了構造器、toString等方法,只專注在這個部分所需要使用到的東西
@override
public int compareTo(Object obj) {
if(this == obj) {
return 0;
}
if(obj instanceOf User) {
User o = (User) obj;
return this.age - o.age;
}
throw new RuntimeException("Wrong Type");
}
TreeSet set = new TreeSet();
User user1 = new User("Tom", 20);
User user2 = new User("Amy", 30);
User user3 = new User("John", 20);
Iterator iterator = set.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
User{name='Tom', age=20}
User{name='Amy', age=30}
由於重寫的compareTo()
方法沒有比較name
屬性,所以就算user1
和user3
的name
屬性不同,TreeSet
還是將它們判斷為重複
的資料。
User中的compareTo()修改
@override
public int compareTo(Object obj) {
if(this == obj) {
return 0;
}
if(obj instanceOf User) {
User o = (User) obj;
int compareValue = this.age - o.age;
if(compareValue != 0) {
return compareValue;
}
return this.name.compareTo(o.name); // 若age相同時,進行name的比較
}
throw new RuntimeException("Wrong Type");
當age
相同時,會進行name
屬性的比較,因為String類
已經有copareTo()
了,所以這邊可以直接透過this.name.compareTo(o.name);
直接使用。
使用Comparator
接口(User基本上只差別在於有沒有Comparable
,因此就直接拿來使用了)
Comparator comparator = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof User && o2 instanceof User) {
User u1 = (User) o1;
User u2 = (User) o2;
int compareName = u1.name.compareTo(u2.name);
if(compareName != 0) {
return compareName;
}
return u1.age - u2.age;
}
throw new RuntimeException("Wrong Type");
}
};
User user1 = new User("Tom", 20);
User user2 = new User("Amy", 30);
User user3 = new User("John", 20);
TreeSet set = new TreeSet(comparator); //需要將實作的comparator放入實例化的參數中
set.add(user1);
set.add(user2);
set.add(user3);
Iterator iterator = set.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
User{name='Amy', age=30}
User{name='John', age=20}
User{name='Tom', age=20}
由於是先進行name
的排序,接著才排序age
,所以這邊排序後,因為沒有相同的問題發生,就會按照name
的順序印出資料。
for
循環(for-each循環)用於Array
、List
、Set
的循環語法糖,會對每一個元素進行迭代
好處:避免越界錯誤(IndexOutOfBoundsException
)、簡潔、不用特別調用Iterator
迭代器。
缺點:無法對索引
直接進行操作
語法
for (Type variable : IterableOrArray) {
// 在每次迭代中使用 variable
}
用法範例
ArrayList list = new ArrayList();
list.add("abc");
list.add("bbb");
list.add(123);
for(Object obj : list) {
System.out.println(obj);
}
abc
bbb
123
需要特別注意的是,因為增強for循環
的底層也是使用Iterator
,所以如果是自定義的類需要使用時,必須先實作Iterator
接口才能這樣使用。